/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-10-26     Tian Yiqing  the first version
 */

#include "drv_hw_i2c.h"
#include <stdio.h>
#include <stdlib.h>

#ifdef BSP_USING_SHT20

#define LOG_TAG              "drv.sht20"
#include <rtdbg.h>

#define SHT20_I2C_ADDRESS (0x80)
#define SHT20_GET_TEMP_CMD (0xF3)
#define SHT20_GET_HUMIDITY_CMD (0xF5)

#define SENSOR_TEMP_RANGE_MAX (85)
#define SENSOR_TEMP_RANGE_MIN (-40)
#define SENSOR_HUMI_RANGE_MAX (100)
#define SENSOR_HUMI_RANGE_MIN (0)

typedef struct sht20_dev
{
   struct rt_i2c_bus_device *i2c_bus;
   rt_sensor_t sensor_temp;
   rt_sensor_t sensor_humi;
   rt_mutex_t lock;
}sht20_dev_t;

static sht20_dev_t sht20_dev;

float sht20_read_temperature(rt_sensor_t sensor)
{
    float result = 0;
    uint8_t cmd;
    uint8_t data[2] = {0};
    uint16_t val;
    struct rt_i2c_msg msg;

    sht20_dev_t *dev = (sht20_dev_t*)sensor->parent.user_data;
    rt_mutex_take(dev->lock, RT_WAITING_FOREVER);

    // Send get temp command
    cmd = SHT20_GET_TEMP_CMD;
    msg.addr = SHT20_I2C_ADDRESS;
    msg.buf = &cmd;
    msg.len = 1;
    msg.flags = RT_I2C_WR;
    if(rt_i2c_transfer(dev->i2c_bus, &msg, 1) != 1) 
    {
        goto __exit;
    }

    rt_thread_mdelay(100);

    // get temp data
    msg.addr = SHT20_I2C_ADDRESS;
    msg.buf = data;
    msg.len = 2;
    msg.flags = RT_I2C_RD;
    if(rt_i2c_transfer(dev->i2c_bus, &msg, 1) != 1) 
    {
        goto __exit;
    }

    val = (data[0] << 8) | data[1];
    if(val & (1<<1))
    {
        LOG_E("Get sht20 temp failed!\r\n");
        goto __exit;
    }

    val = val & 0xFFFC;
    result = (-46.85f + 175.72f*((float)val)/65536.0f);

__exit:
    rt_mutex_release(dev->lock);
    return result;
}

float sht20_read_humidity(rt_sensor_t sensor)
{
    float result = 0;

    uint8_t cmd;
    uint16_t val;
    uint8_t data[2] = {0};
    struct rt_i2c_msg msg;

    sht20_dev_t *dev = (sht20_dev_t*)sensor->parent.user_data;
    rt_mutex_take(dev->lock, RT_WAITING_FOREVER);

    // Send get humidity command
    cmd = SHT20_GET_HUMIDITY_CMD;
    msg.addr = SHT20_I2C_ADDRESS;
    msg.buf = &cmd;
    msg.len = 1;
    msg.flags = RT_I2C_WR;
    if(rt_i2c_transfer(dev->i2c_bus, &msg, 1) != 1) 
    {
        goto __exit;
    }

    rt_thread_mdelay(100);

    // get humidity data
    msg.addr = SHT20_I2C_ADDRESS;
    msg.buf = data;
    msg.len = 2;
    msg.flags = RT_I2C_RD;
    if(rt_i2c_transfer(dev->i2c_bus, &msg, 1) != 1) 
    {
        goto __exit;
    }

    val = (data[0] << 8) | data[1];
    if(val & (1<<1))
    {
        val = val & 0xFFFC;
        result = (-6.0f + 125.0f*((float)val)/65536.0f);
    }
    else
    {
        LOG_E("Get sht20 humidity failed!\r\n");
    }

__exit:
    rt_mutex_release(dev->lock);
    return result;
}

static rt_size_t sht20_polling_get_data(rt_sensor_t sensor, struct rt_sensor_data *data)
{
    float temperature_x10, humidity_x10;

    if (sensor->info.type == RT_SENSOR_CLASS_TEMP)
    {
        temperature_x10 = 10 * sht20_read_temperature(sensor);
        data->data.temp = (rt_int32_t)temperature_x10;
        data->timestamp = rt_sensor_get_ts();
    }
    else if (sensor->info.type == RT_SENSOR_CLASS_HUMI)
    {
        humidity_x10    = 10 * sht20_read_humidity(sensor);
        data->data.humi = (rt_int32_t)humidity_x10;
        data->timestamp = rt_sensor_get_ts();
    }
    return 1;
}

rt_size_t sht20_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    if (sensor->config.mode == RT_SENSOR_MODE_POLLING)
    {
        return sht20_polling_get_data(sensor, buf);
    }
    else
    {
        return 0;
    }
}

rt_err_t sht20_control(struct rt_sensor_device *sensor, int cmd, void *arg)
{
    return RT_EOK;
}

static struct rt_sensor_ops sensor_ops =
{
    sht20_fetch_data,
    sht20_control
};

/**
  * @brief  SHT20 initialization function
  * @param  None
  * @retval RT_EOK indicates successful initialization.
  */
int rt_hw_sht20_init(void)
{
    int result;
    rt_sensor_t sensor_temp = RT_NULL, sensor_humi = RT_NULL;

    sht20_dev.i2c_bus = rt_i2c_bus_device_find("i2c0");
    if (sht20_dev.i2c_bus == RT_NULL)
    {
        LOG_E("Can't find aht10 device on i2c0");
        return -1;
    }

    sht20_dev.lock = rt_mutex_create("sht20_lock", RT_IPC_FLAG_FIFO);
    if (sht20_dev.lock == RT_NULL)
    {
        LOG_E("Can't create mutex for sht20 device failed");
        return -1;
    }

    sensor_temp = rt_calloc(1, sizeof(struct rt_sensor_device));
    if(sensor_temp == RT_NULL)
        return -1;
    
    sensor_temp->info.type = RT_SENSOR_CLASS_TEMP;
    sensor_temp->info.vendor = RT_SENSOR_VENDOR_UNKNOWN;
    sensor_temp->info.model = "sht20";
    sensor_temp->info.unit = RT_SENSOR_UNIT_DCELSIUS;
    sensor_temp->info.intf_type = RT_SENSOR_INTF_I2C;
    sensor_temp->info.range_max = SENSOR_TEMP_RANGE_MAX;
    sensor_temp->info.range_min = SENSOR_TEMP_RANGE_MIN;
    sensor_temp->info.period_min = 100;
    sensor_temp->config.intf.dev_name = "i2c0";
    sensor_temp->config.intf.type = RT_SENSOR_INTF_I2C;
    sensor_temp->config.intf.user_data = (void*)SHT20_I2C_ADDRESS;
    sensor_temp->ops = &sensor_ops;

    result = rt_hw_sensor_register(sensor_temp, "sht20", RT_DEVICE_FLAG_RDONLY, &sht20_dev);
    if (result != RT_EOK)
    {
        LOG_E("sensor temp device register err code: %d", result);
        goto __exit;
    }

    sensor_humi = rt_calloc(1, sizeof(struct rt_sensor_device));
    if(sensor_humi == RT_NULL)
    {
        result = -1;
        goto __exit;
    }

    sensor_humi->info.type = RT_SENSOR_CLASS_HUMI;
    sensor_humi->info.vendor = RT_SENSOR_VENDOR_UNKNOWN;
    sensor_humi->info.model = "sht20";
    sensor_humi->info.unit = RT_SENSOR_UNIT_PERMILLAGE;
    sensor_humi->info.intf_type = RT_SENSOR_INTF_I2C;
    sensor_humi->info.range_max = SENSOR_HUMI_RANGE_MAX;
    sensor_humi->info.range_min = SENSOR_HUMI_RANGE_MIN;
    sensor_humi->info.period_min = 100;
    sensor_humi->config.intf.dev_name = "i2c0";
    sensor_humi->config.intf.type = RT_SENSOR_INTF_I2C;
    sensor_humi->config.intf.user_data = (void*)SHT20_I2C_ADDRESS;
    sensor_humi->ops = &sensor_ops;

    result = rt_hw_sensor_register(sensor_humi, "sht20", RT_DEVICE_FLAG_RDONLY, &sht20_dev);
    if(result != RT_EOK)
    {
        LOG_E("sensor humi device register err code: %d", result);
        goto __exit;
    }

    sht20_dev.sensor_temp = sensor_temp;
    sht20_dev.sensor_humi = sensor_humi;

    return RT_EOK;

__exit:
    if(sensor_temp)
        rt_free(sensor_temp);
    if(sensor_humi)
        rt_free(sensor_humi);

    return result;
}
INIT_DEVICE_EXPORT(rt_hw_sht20_init);

#endif /* BSP_USING_SHT20 */
